﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Utilities.Excel.Xml;
using System.Reflection;
using System.Xml;

namespace Common.Utilities.Excel.Export
{
    public abstract class CellStyleBase
    {
        #region Properties
        /// <summary>
        /// 字体的唯一标识
        /// </summary>
        public string ID { get; protected set; }

        /// <summary>
        /// 被设置的属性, 配置方法Properties1;Properties2;Properties3;Properties4...
        /// </summary>
        public List<string> Properties { get; protected set; }

        /// <summary>
        /// 设置到对应属性的值, 配置方法如String:1;Byte:2;Short:3;Reflect:Assembly,Class,Field@NPOI.HSSF,NPOI.HSSF.UserModel.HSSFCellStyle,BORDER_DOTTED
        /// 可选数据类型为: String, Byte, Boolean, Short, Reflect
        /// </summary>
        public List<Argument> Arguments { get; protected set; }

        #endregion

        #region ctor

        public CellStyleBase(XmlNode baseNode)
        {
            ID = XmlUtility.getNodeAttributeStringValue(baseNode, "id");
            InitPropertiesAndArguments(baseNode);
        }

        #endregion

        #region Methods

        /// <summary>
        /// 初始化Properties和Arguments
        /// </summary>
        /// <param name="fontNode"></param>
        protected void InitPropertiesAndArguments(XmlNode baseNode)
        {
            Properties = new List<string>();
            Arguments = new List<Argument>();

            string propertiesConfiged = XmlUtility.getNodeAttributeStringValue(baseNode, "properties", "");
            string[] propertiesConfigedArray = propertiesConfiged.Split(';');

            string argumentsConfiged = XmlUtility.getNodeAttributeStringValue(baseNode, "arguments", "");
            string[] argumentsConfigedArray = argumentsConfiged.Split(';');
            // 不匹配则抛异常
            if (propertiesConfigedArray.Length != argumentsConfigedArray.Length)
                throw new Exception("Arguments 和 Properties 配置的数量不匹配");
            // 遍历要设置的属性数组, 添加到配置属性中
            foreach (string propertyConfiged in propertiesConfigedArray)
            {
                if (!string.IsNullOrEmpty(propertyConfiged))
                    Properties.Add(propertyConfiged.Trim());
            }
            // 遍历设置到属性的值的数组, 添加到配置参数中
            foreach (string argumentConfiged in argumentsConfigedArray)
            {
                if (!string.IsNullOrEmpty(argumentConfiged))
                    Arguments.Add(InitArgument(argumentConfiged.Trim()));
            }
        }

        /// <summary>
        /// 根据特定的格式, 初始化一个Argument配置项
        /// </summary>
        /// <param name="argumentConfiged"></param>
        /// <returns></returns>
        protected Argument InitArgument(string argumentConfiged)
        {
            ArgumentTypeEnums argumentType;
            // 参数的每一项目由这样的格式组成: "数据类型枚举值:被赋的值"
            string[] argumentConfigArray = argumentConfiged.Split(':');
            // 获取数据类型枚举值argumentConfigArray[0]
            switch (argumentConfigArray[0].Trim())
            {
                case "Short":
                    argumentType = ArgumentTypeEnums.Short;
                    break;
                case "Byte":
                    argumentType = ArgumentTypeEnums.Byte;
                    break;
                case "Boolean":
                    argumentType = ArgumentTypeEnums.Boolean;
                    break;
                case "Reflect":
                    argumentType = ArgumentTypeEnums.Reflect;
                    break;
                case "String":
                default:
                    argumentType = ArgumentTypeEnums.String;
                    break;
            }
            // 获取数据值
            string configValue = argumentConfigArray[1].Trim();
            return new Argument(argumentType, configValue);
        }
        #endregion
    }

    public class Argument
    {
        #region Properties

        /// <summary>
        /// 参数的数据类型
        /// </summary>
        public ArgumentTypeEnums ArgumentType { get; set; }
        /// <summary>
        /// 参数的值
        /// </summary>
        public string ConfigValue { get; set; }

        #endregion

        #region ctor

        public Argument(ArgumentTypeEnums argumentType, string configValue)
        {
            ArgumentType = argumentType;
            ConfigValue = configValue;
        }

        #endregion

        #region Methods

        /// <summary>
        /// 根据数据类型枚举, 获取对应数据类型的值
        /// </summary>
        /// <returns></returns>
        public object GetValue()
        {
            switch (ArgumentType)
            {
                case ArgumentTypeEnums.String:
                default:
                    return ConfigValue;
                case ArgumentTypeEnums.Short:
                    return short.Parse(ConfigValue);
                case ArgumentTypeEnums.Byte:
                    return byte.Parse(ConfigValue);
                case ArgumentTypeEnums.Boolean:
                    return bool.Parse(ConfigValue);
                case ArgumentTypeEnums.Reflect:  // 通过反射获取配置的值
                    return GetReflectValue();
            }
        }

        /// <summary>
        /// 通过反射获取配置的值(静态方法, 静态属性, 静态字段)
        /// </summary>
        /// <returns></returns>
        public object GetReflectValue()
        {
            // 通过反射方式获取的值的配置方法为: 
            // "[Assembly,]Class,Property|Field|Function[,Property|Field|Function[,...]]@AssemblyName,ClassName,PrpertyName|FieldName|FunctionName[,PrpertyName|FieldName|FunctionName[,...]]"
            string[] argumentArray = ConfigValue.Split('@');
            string[] reflectArray = argumentArray[0].Split(','); // 按逗号为分隔, 把Assembly, Class 等的关键内容分隔出
            string[] reflectValueArray = argumentArray[1].Split(','); // 按逗号为分隔, 把Assembly, Class 等的值分割出
            object obj = null; // 存放迭代时的中间值
            for (int iLoop = 0; iLoop < reflectArray.Length; iLoop++)
            {
                switch (reflectArray[iLoop])
                {
                    case "Assembly": // 如果是程序集, 则加载程序集
                        Assembly assembly = Assembly.Load(reflectValueArray[iLoop]);
                        obj = assembly;
                        break;
                    case "Class": // 如果是类, 则获取类的类型(Type)
                        Type type;
                        if (obj != null) // 如果obj != null, 则obj必为程序集(因为在类前面的只有程序集合可被配置)
                            type = (obj as Assembly).GetType(reflectValueArray[iLoop].Trim());
                        else // 如果obj == null, 则为没有配置程序集, 这时只需直接GetType
                            type = Type.GetType(reflectValueArray[iLoop]);
                        obj = type;
                        break;
                    case "Property": // 如果是属性, 则获取其属性的值
                        Type typePro;
                        if (obj is Type) // 如果obj是类型, 则前一迭代比为类, 类的最终将为obj赋上类型Type, 所以只需直接转换
                            typePro = obj as Type;
                        else
                            typePro = obj.GetType();  // 如果obj不是类型, 则obj为上一次获取属性或调用方法或获取字段得到的内容, 即比为一个对象, 所以调用obj.GetType()来获取其类型, 以便进行反射
                        PropertyInfo propertyInfo = typePro.GetProperty(reflectValueArray[iLoop].Trim()); // 获取属性信息
                        obj = propertyInfo.GetValue(obj, null); // 获取属性的值
                        break;
                    case "Field": // 如果是字段, 则获取其字段的值
                        Type typeField;
                        if (obj is Type) 
                            typeField = obj as Type;
                        else
                            typeField = obj.GetType();
                        FieldInfo fieldInfo = typeField.GetField(reflectValueArray[iLoop].Trim()); // 获取字段的信息
                        obj = fieldInfo.GetValue(obj); // 获取字段的值
                        break;
                    case "Function":
                        Type typeFun;
                        if (obj is Type)
                            typeFun = obj as Type;
                        else
                            typeFun = obj.GetType();
                        MethodInfo methodInfo = typeFun.GetMethod(reflectValueArray[iLoop].Trim()); // 获取方法的信息
                        obj = methodInfo.Invoke(obj, null); // 调用方法获得返回值
                        break;
                }
            }
            return obj;
        }

        #endregion
    }
}
